#include "../include/head.h"
/* #include "../include/head.h" */
#include <errno.h>

int server_cd(const RequestPackage *prequest, char *serverDir, int netfd) {
    // 1. 需要解析requestPackage
    ResponsePackage response;
    bzero(&response, sizeof(response));
    /* printf("成功进入cd!\n"); */
    char vir_path[1024] = {0}; // 例如/dir1/dir2
    memcpy(vir_path, prequest->vir_path_val, prequest->vir_path_len);
    char args_path[1024] = {0};     // 例如dir3/../dir4/../dir5   ../../../../
    memcpy(args_path, prequest->args_val, prequest->args_len);
    // 2. 得到当前虚拟工作目录，与服务端当前目录结合，验证虚拟工作目录是否合法
    //     若不合法则发送response包，含有错误标识，若合法则继续
    char truePath[1024] = {0};
    sprintf(truePath, "%s%s", serverDir, vir_path);
    // 测试的打印信息
    // printf("serverPath+virtualPath = %s \n", truePath);
    if(access(truePath, F_OK)) {
        // 说明该目录不存在
        response.type = RES_ERR_1;
        response.vir_path_len = 0;
        ssize_t sret = snprintf(response.args_val, sizeof(response.args_val), "当前目录结构已更改，已返回到根目录!");
        response.args_len = sret;
        // printf("说明虚拟路径不存在!\n");
        // 这里需要分开发
        send(netfd, &response.type, sizeof(response.type), MSG_NOSIGNAL);
        send(netfd, &response.vir_path_len, sizeof(int), MSG_NOSIGNAL);
        send(netfd, &response.args_len, sizeof(int), MSG_NOSIGNAL);
        send(netfd, response.args_val, response.args_len, MSG_NOSIGNAL);
        return 0;
    }
    // 3. 通过一个链栈，将虚拟目录与参数目录都压栈得到一个最终的虚拟路径，然后与
    // 服务器当前目录结合，通过能够打开查看是否正确，最终发送一个最终的虚拟路径
    ServerVirtualPathStack *pstack = ServerVirtualPathStack_init();
    // 我需要写一个函数，将路径拆分装入链栈中，得到一个最终的虚拟路径
    if(args_path[0] != '/') {
        pathTostack(pstack, vir_path);
    }
    pathTostack(pstack, args_path);

    // 然后再转换成字符串与服务端路径拼接在一起，通过access查看是否存在
    bzero(truePath, sizeof(truePath));
    bzero(vir_path, sizeof(vir_path));
    ServerVirtualPathStack_print(pstack, vir_path);
    sprintf(truePath, "%s%s", serverDir, vir_path);
    struct stat path_stat;
    // bug: 这里没有考虑到可能进入的普通文件，而不是目录，所以不能简单的使用access判断文件是否存在
    // 需要使用stat判断能否打开文件，不能则不存在，若可以继续判断文件的模式，是不是目录,S_ISDIR宏函数返回1则是目录
    if(stat(truePath, &path_stat) != 0) {
        // 说明文件不存在或者无法访问
        response.type = RES_ERR_2;
        response.vir_path_len = 0;
        ssize_t sret = snprintf(response.args_val, sizeof(response.args_val), "目标目录不存在，请检查后重新输入正确路径!");
        response.args_len = sret;
        send(netfd, &response.type, sizeof(response.type), MSG_NOSIGNAL);
        send(netfd, &response.vir_path_len, sizeof(int), MSG_NOSIGNAL);
        send(netfd, &response.args_len, sizeof(int), MSG_NOSIGNAL);
        send(netfd, response.args_val, response.args_len, MSG_NOSIGNAL);
        // printf("说明参数路径不存在!\n");
        return 0;
    } else {
        if(S_ISDIR(path_stat.st_mode) != 1) {
            // 说明这不是目录，而是普通文件
            response.type = RES_ERR_2;
            response.vir_path_len = 0;
            ssize_t sret = snprintf(response.args_val, sizeof(response.args_val), "目标是普通文件，而不是目录，请输入目标目录的正确路径!");
            response.args_len = sret;
            send(netfd, &response.type, sizeof(response.type), MSG_NOSIGNAL);
            send(netfd, &response.vir_path_len, sizeof(int), MSG_NOSIGNAL);
            send(netfd, &response.args_len, sizeof(int), MSG_NOSIGNAL);
            send(netfd, response.args_val, response.args_len, MSG_NOSIGNAL);
            // printf("说明参数路径不存在!\n");
            return 0;
        } else {
            response.type = RES_CD;
            response.vir_path_len = strlen(vir_path);
            memcpy(response.vir_path_val, vir_path, strlen(vir_path));
            response.args_len = 0;
            send(netfd, &response.type, sizeof(response.type), MSG_NOSIGNAL);
            send(netfd, &response.vir_path_len, sizeof(int), MSG_NOSIGNAL);
            send(netfd, response.vir_path_val, response.vir_path_len, MSG_NOSIGNAL);
            send(netfd, &response.args_len, sizeof(int), MSG_NOSIGNAL);
            // printf("说明参数路径成功! vir_path = %s \n", response.vir_path_val);
        }
    }
    /* if(access(truePath, F_OK)) { */
    /*     // 说明args_path不存在 */
    /*     response.type = RES_ERR_2; */
    /*     response.vir_path_len = 0; */
    /*     ssize_t sret = snprintf(response.args_val, sizeof(response.args_val), "目标目录不存在，请检查后重新输入正确路径!"); */
    /*     response.args_len = sret; */
    /*     send(netfd, &response.type, sizeof(response.type), MSG_NOSIGNAL); */
    /*     send(netfd, &response.vir_path_len, sizeof(int), MSG_NOSIGNAL); */
    /*     send(netfd, &response.args_len, sizeof(int), MSG_NOSIGNAL); */
    /*     send(netfd, response.args_val, response.args_len, MSG_NOSIGNAL); */
    /*     printf("说明参数路径不存在!\n"); */
    /*     return 0; */
    /* } */
    // 其实这里需要free链栈申请的空间
    return 0;
}

int pathTostack(ServerVirtualPathStack *pstack, char *path) {
    // 虚拟工作路径是/dir1/dir2，拆分使用/作为分界
    // 参数路径是dir3/../dir4/../dir5
    char *token;     // 切割出来的字符串
    char *saveptr;  // 保存切割完后的剩余字符串
                    // 必须要考虑到path是空字符串，如何处理
    for(char *str = path; ; str = NULL) {
        token = strtok_r(str, "/", &saveptr);
        if(token == NULL) break;
        else if(strcmp(token, ".") == 0) {
            continue;
        } else if(strcmp(token, "..") == 0) {
            if(pstack->size == 0) {
                continue;
            } else {
                ServerVirtualPathStack_pop(pstack);
            }
        } else {
            ServerVirtualPathStack_push(pstack, token);
        }
    }

    return 0;
}

// int main(void) {
//     // 需要做stack_op.c 和 pathTostack的单元测试
//     char path[1024] = {0};
//     char resultPath[1024] = {0};
//     ServerVirtualPathStack stack;
//     while(1) {
//         bzero(path, sizeof(path));
//         fgets(path, sizeof(path), stdin);
//         path[strlen(path)-1] = '\0';
//         pathTostack(&stack, path);
//         bzero(resultPath, sizeof(resultPath));
//         ServerVirtualPathStack_print(&stack, resultPath);
//         if(strlen(resultPath) == 0) 
//             printf("result = %s \n", "/");
//         else 
//             printf("resultPath = %s \n", resultPath);
//     }

//     return 0;
// }

//成功ls返回0，失败返回-1
//serverDir应该为整个server的全局函数，在具体的电脑上需要改路径，这个下面的serverDir只用来测试
/* char serverDir[] = {"/home/sb/Linux/7.17networkDisk/gg-wp/server"}; */
int server_ls(const RequestPackage *prequest, char *serverDir, int netfd){
    //全局变量路径拼接
    // printf("ls:%d\n", netfd);
    char path[4096] = {0};
    char subpath[1024];
    size_t slen = prequest->vir_path_len;
    if (slen >= sizeof(subpath)) slen = sizeof(subpath) - 1;
    memcpy(subpath, prequest->vir_path_val, slen);
    subpath[slen] = '\0';
    snprintf(path, sizeof(path), "%s/%s", serverDir, subpath);
    DIR *dp = opendir(path);
    //虚拟路径有误
    if(dp == NULL){
        ResponsePackage psponse;
        memset(&psponse, 0, sizeof psponse);
        psponse.type = RES_ERR_1;
        psponse.vir_path_len = 0;
        bzero(psponse.vir_path_val, sizeof(psponse.vir_path_val));
        sprintf( psponse.args_val, "目录已被修改, ls 失败: %s\n",  strerror(errno));
        psponse.args_len = strlen(psponse.args_val);
        // printf("%s\n",  psponse.args_val);
        send(netfd, &psponse.type, sizeof(psponse.type), MSG_NOSIGNAL);
        send(netfd, &psponse.vir_path_len, sizeof(psponse.vir_path_len), MSG_NOSIGNAL);
        send(netfd, &psponse.args_len, sizeof(psponse.args_len), MSG_NOSIGNAL);
        send(netfd, psponse.args_val, psponse.args_len, MSG_NOSIGNAL);
        return -1;
    }
    //虚拟路径正确，把ls内容打包发送
    struct dirent *entry;
    ResponsePackage psponse;
    memset(&psponse, 0, sizeof psponse);
    int pos = 0;
    while ((entry = readdir(dp)) != NULL) {
        int n = snprintf(psponse.args_val + pos,sizeof psponse.args_val - pos, "%s\t", entry->d_name);
        size_t written = 0;
        if (n > 0) {
            written = (size_t)n < sizeof psponse.args_val - pos
                ? (size_t)n
                : sizeof psponse.args_val - pos - 1;
            pos += written;             
        }
        psponse.args_len = pos;         
    }
    psponse.args_val[pos] = '\0';
    psponse.vir_path_len = 0;
    psponse.type = RES_LS;
    // printf("ls sent type %d\n", psponse.type);
    send(netfd, &psponse.type, sizeof(psponse.type), MSG_NOSIGNAL);
    send(netfd, &psponse.vir_path_len, sizeof(psponse.vir_path_len), MSG_NOSIGNAL);
    send(netfd, &psponse.args_len, sizeof(psponse.args_len), MSG_NOSIGNAL);
    send(netfd, psponse.args_val, psponse.args_len, MSG_NOSIGNAL);
    closedir(dp);
    return 0;
}





int server_pwd(const RequestPackage *prequest, char *serverDir, int netfd){
    char path[4096] = {0};
    //全局变量路径拼接
    char subpath[1024];
    size_t slen = prequest->vir_path_len;
    if (slen >= sizeof(subpath)) slen = sizeof(subpath) - 1;
    memcpy(subpath, prequest->vir_path_val, slen);
    subpath[slen] = '\0';
    snprintf(path, sizeof(path), "%s/%s", serverDir, subpath);
    DIR *dp = opendir(path);
    //虚拟路径有误
    if(dp == NULL){
        ResponsePackage psponse;
        memset(&psponse, 0, sizeof psponse);
        psponse.type = RES_ERR_1;
        psponse.vir_path_len = 0;
        bzero(psponse.vir_path_val, sizeof(psponse.vir_path_val));
        sprintf( psponse.args_val, "目录已被修改, pwd 失败: %s\n",  strerror(errno));
        psponse.args_len = strlen(psponse.args_val);
        // printf("%s\n",  psponse.args_val);
        send(netfd, &psponse.type, sizeof(psponse.type), MSG_NOSIGNAL);
        send(netfd, &psponse.vir_path_len, sizeof(psponse.vir_path_len), MSG_NOSIGNAL);
        send(netfd, &psponse.args_len, sizeof(psponse.args_len), MSG_NOSIGNAL);
        send(netfd, psponse.args_val, psponse.args_len, MSG_NOSIGNAL);
        return -1;
    }

    ResponsePackage psponse;
    memset(&psponse, 0, sizeof psponse);
    /* char buf[1024] = {0}; */
    //使用getcwd不稳定，返回的是当前目录并不是到用户目录
    /* getcwd(buf, sizeof(buf)); */
    /* memcpy(psponse.args_val, buf + strlen(serverDir), strlen(buf) - strlen(serverDir)); */
    memcpy(psponse.args_val, subpath, strlen(subpath));
    psponse.args_len = strlen(psponse.args_val);
    psponse.type = RES_PWD;
    // printf("%s\n", psponse.args_val);
    send(netfd, &psponse.type, sizeof(psponse.type), MSG_NOSIGNAL);
    send(netfd, &psponse.vir_path_len, sizeof(psponse.vir_path_len),MSG_NOSIGNAL );
    send(netfd, &psponse.args_len, sizeof(psponse.args_len), MSG_NOSIGNAL);
    send(netfd, psponse.args_val, psponse.args_len, MSG_NOSIGNAL);
    closedir(dp);
    return 0;
}


int server_mkdir(const RequestPackage *prequest, char *serverDir, int netfd) {
    //准备子路径
    char subpath[1024];
    size_t slen = prequest->vir_path_len;
    if (slen >= sizeof(subpath)) slen = sizeof(subpath) - 1;
    memcpy(subpath, prequest->vir_path_val, slen);
    subpath[slen] = '\0';

    //拼接真实路径
    char path[2048];
    int n = snprintf(path, sizeof path, "%s/%s", serverDir, subpath);
    if (n < 0 || (size_t)n >= sizeof path) {
        ResponsePackage rsp ;
        memset(&rsp, 0, sizeof rsp);
        rsp.type = RES_ERR_2;
        rsp.args_len = snprintf(rsp.args_val, sizeof rsp.args_val, "mkdir 失败\n");
        send(netfd, &rsp.type, sizeof(rsp.type), MSG_NOSIGNAL);
        send(netfd, &rsp.vir_path_len, sizeof(rsp.vir_path_len), MSG_NOSIGNAL);
        send(netfd, &rsp.args_len, sizeof(rsp.args_len), MSG_NOSIGNAL);
        send(netfd, rsp.args_val, rsp.args_len, MSG_NOSIGNAL);
        return -1;
    }

    DIR *dp = opendir(path);
    //虚拟路径有误
    if(dp == NULL){
        ResponsePackage psponse;
        memset(&psponse, 0, sizeof psponse);
        psponse.type = RES_ERR_1;
        psponse.vir_path_len = 0;
        bzero(psponse.vir_path_val, sizeof(psponse.vir_path_val));
        sprintf( psponse.args_val, "目录已被修改, ls 失败: %s\n",  strerror(errno));
        psponse.args_len = strlen(psponse.args_val);
        // printf("%s\n",  psponse.args_val);
        send(netfd, &psponse.type, sizeof(psponse.type), MSG_NOSIGNAL);
        send(netfd, &psponse.vir_path_len, sizeof(psponse.vir_path_len), MSG_NOSIGNAL);
        send(netfd, &psponse.args_len, sizeof(psponse.args_len), MSG_NOSIGNAL);
        send(netfd, psponse.args_val, psponse.args_len, MSG_NOSIGNAL);
        return -1;
    }


    // 调用 mkdir
    snprintf(path+n, sizeof path-n, "/%s", prequest->args_val);
    if (mkdir(path, 0755) < 0) {
        ResponsePackage rsp;
        memset(&rsp, 0, sizeof rsp);
        rsp.type = RES_ERR_2;
        rsp.args_len = snprintf(rsp.args_val, sizeof rsp.args_val, "mkdir 失败：%s\n", strerror(errno));
        send(netfd, &rsp.type, sizeof(rsp.type), MSG_NOSIGNAL);
        send(netfd, &rsp.vir_path_len, sizeof(rsp.vir_path_len), MSG_NOSIGNAL);
        send(netfd, &rsp.args_len, sizeof(rsp.args_len), MSG_NOSIGNAL);
        send(netfd, rsp.args_val, rsp.args_len, MSG_NOSIGNAL);
        return -1;
    }

    //成功响应
    ResponsePackage rsp;
    memset(&rsp, 0, sizeof rsp);
    rsp.type = RES_MKDIR;
    // 客户端只需知道创建的虚拟路径
    strncpy(rsp.args_val, subpath, sizeof rsp.args_val - 1);
    rsp.args_len = strlen(rsp.args_val);
    send(netfd, &rsp.type, sizeof(rsp.type), MSG_NOSIGNAL);
    send(netfd, &rsp.vir_path_len, sizeof(rsp.vir_path_len), MSG_NOSIGNAL);
    send(netfd, &rsp.args_len, sizeof(rsp.args_len), MSG_NOSIGNAL);
    send(netfd, rsp.args_val, rsp.args_len, MSG_NOSIGNAL);
    return 0;
}

// 删除目录：成功返回 0，失败返回 -1
int server_rmdir(const RequestPackage *prequest, char *serverDir, int netfd) {
    //准备子路径
    char subpath[1024];
    size_t slen = prequest->vir_path_len;
    if (slen >= sizeof(subpath)) slen = sizeof(subpath) - 1;
    memcpy(subpath, prequest->vir_path_val, slen);
    subpath[slen] = '\0';

    //拼接真实路径
    char path[2048];
    int n = snprintf(path, sizeof path, "%s/%s", serverDir, subpath);
    if (n < 0 || (size_t)n >= sizeof path) {
        ResponsePackage rsp;
        memset(&rsp, 0, sizeof rsp);
        rsp.type = RES_ERR_2;
        rsp.args_len = snprintf(rsp.args_val, sizeof rsp.args_val, "rmdir 失败：\n");
        send(netfd, &rsp.type, sizeof(rsp.type), MSG_NOSIGNAL);
        send(netfd, &rsp.vir_path_len, sizeof(rsp.vir_path_len), MSG_NOSIGNAL);
        send(netfd, &rsp.args_len, sizeof(rsp.args_len), MSG_NOSIGNAL);
        send(netfd, rsp.args_val, rsp.args_len, MSG_NOSIGNAL);
        return -1;
    }
    DIR *dp = opendir(path);
    //虚拟路径有误
    if(dp == NULL){
        ResponsePackage psponse;
        memset(&psponse, 0, sizeof psponse);
        psponse.type = RES_ERR_1;
        psponse.vir_path_len = 0;
        bzero(psponse.vir_path_val, sizeof(psponse.vir_path_val));
        sprintf( psponse.args_val, "目录已被修改, ls 失败: %s\n",  strerror(errno));
        psponse.args_len = strlen(psponse.args_val);
        // printf("%s\n",  psponse.args_val);
        send(netfd, &psponse.type, sizeof(psponse.type), MSG_NOSIGNAL);
        send(netfd, &psponse.vir_path_len, sizeof(psponse.vir_path_len), MSG_NOSIGNAL);
        send(netfd, &psponse.args_len, sizeof(psponse.args_len), MSG_NOSIGNAL);
        send(netfd, psponse.args_val, psponse.args_len, MSG_NOSIGNAL);
        return -1;
    }

    //调用 rmdir
    snprintf(path+n, sizeof path-n, "/%s", prequest->args_val);
    if (rmdir(path) < 0) {
        ResponsePackage rsp;
        memset(&rsp, 0, sizeof rsp);
        rsp.type = RES_ERR_2;
        rsp.args_len = snprintf(rsp.args_val, sizeof rsp.args_val, "rmdir 失败：%s\n", strerror(errno));
        send(netfd, &rsp.type, sizeof(rsp.type), MSG_NOSIGNAL);
        send(netfd, &rsp.vir_path_len, sizeof(rsp.vir_path_len), MSG_NOSIGNAL);
        send(netfd, &rsp.args_len, sizeof(rsp.args_len), MSG_NOSIGNAL);
        send(netfd, rsp.args_val, rsp.args_len, MSG_NOSIGNAL);
        return -1;
    }

    //成功响应
    ResponsePackage rsp;
    memset(&rsp, 0, sizeof rsp);
    rsp.type = RES_RMDIR;
    strncpy(rsp.args_val, subpath, sizeof rsp.args_val - 1);
    rsp.args_len = strlen(rsp.args_val);
    send(netfd, &rsp.type, sizeof(rsp.type), MSG_NOSIGNAL);
    send(netfd, &rsp.vir_path_len, sizeof(rsp.vir_path_len), MSG_NOSIGNAL);
    send(netfd, &rsp.args_len, sizeof(rsp.args_len), MSG_NOSIGNAL);
    send(netfd, rsp.args_val, rsp.args_len, MSG_NOSIGNAL);
    return 0;
}






//ls 单元测试
/* int main(int argc, char **argv){ */
/*     RequestPackage prequest; */
/*     bzero(&prequest, sizeof(prequest)); */
/*     prequest.type = C_OP_LS; */
/*     prequest.args_len = 2; */
/*     prequest.vir_path_len = strlen(argv[1]); */
/*     memcpy(prequest.vir_path_val, argv[1], 1024); */
/*     /1* server_ls(&prequest, 1); *1/ */
/*     server_pwd(&prequest, 1); */
/* } */

